package com.jd.finance.oss.service.impl;

import com.jd.finance.oss.exception.OSSException;
import com.jd.finance.oss.mapper.OssObjectMapper;
import com.jd.finance.oss.model.OssObject;
import com.jd.finance.oss.model.User;
import com.jd.finance.oss.service.FileService;
import com.jd.finance.oss.task.AsyncTask;
import com.jd.finance.oss.util.AliyunOSSUtils;
import com.jd.finance.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/**
 * @ClassName FileServiceImpl
 * @Description TODO
 * @Author LiQingBo
 * @Date 2022/5/29 10:01
 */
@Slf4j
@Service
@Transactional
public class FileServiceImpl implements FileService {

    @Resource
    OssObjectMapper ossObjectMapper;

    @Autowired
    AsyncTask asyncTask;

    @Override
    public void removeFile(String url) {

    }

    /**
     * 文件上传至阿里云
     *
     * @param file   上传的对象
     * @param module 业务类型
     * @param user   上传的用户
     * @return
     */
    @Override
    public List<OssObject> upload(MultipartFile[] file, String module, User user) throws OSSException {
        long time0 = System.currentTimeMillis();
        //存放插入数据库成功的实体类
        List<OssObject> ossObjectList = new ArrayList<>();
        //上传文件
        for (MultipartFile multipartFile : file) {
            try {
                //当前时间戳
                long time1 = System.currentTimeMillis();
                //构建OSS实体类
                OssObject ossObject = buildOSSObject(multipartFile, module, user);
                log.info("user:{}", user);
                long time2 = System.currentTimeMillis();
                log.info("uploadOSSObjectsTime:2-:{}", time2 - time1);
                //调用阿里云SDK上传对象
                Object[] objects = AliyunOSSUtils.uploadObject(multipartFile, ossObject.getObjectKey());
                // 文件大小
                Long size = (Long) objects[1];
                // 上传是否成功
                boolean res = (boolean) objects[0];
                long time3 = System.currentTimeMillis();
                log.info("uploadOSSObjectsTime:3-:{}", time3 - time2);
                if (res && size > 0) {
                    ossObject.setObjectSize(size + "");
                    //插入到数据库
                    int count = ossObjectMapper.insert(ossObject);
                    if (count < 1) {
                        log.error("对象上传时保存数据库失败：FileName = " + multipartFile.getOriginalFilename());
                        OSSException ossException = new OSSException(-1, "对象上传时保存数据库失败");
                        throw ossException;
                    } else {
                        // 异步保存原图(文件大于10M压缩处理)
                        if (AliyunOSSUtils.ifCompress(multipartFile)) {
                            asyncTask.uploadObjectsOriginalAsync(multipartFile.getInputStream(), multipartFile, ossObject, module, user, Thread.currentThread().getId());
                        }
                        //获取签名的URL，objectUrl
                        String objectUrl = (AliyunOSSUtils.getPreviewUrl(ossObject.getObjectKey())).toString();
                        ossObject.setObjectUrl(objectUrl);
                        //不暴露objectKey
                        ossObject.setObjectKey(null);
                        //添加到返回列表
                        ossObjectList.add(ossObject);
                    }
                } else {
                    log.error("对象上传阿里云OSS失败：FileName = " + multipartFile.getOriginalFilename());
                    OSSException ossException = new OSSException(-3, "对象上传阿里云OSS失败");
                    throw ossException;
                }
                long time4 = System.currentTimeMillis();
                log.info("uploadOSSObjectsTime:4-:{}", time4 - time3);
                log.info("uploadOSSObjectsTime:all:{}", time4 - time1);
                log.info("uploadOSSObjectsTime：{}", cn.hutool.core.date.DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss.sss"));
            } catch (IOException e) {
                log.error("对象上传内部异常：FileName = " + multipartFile.getOriginalFilename(), e);
                OSSException ossException = new OSSException(-4, "对象上传内部异常");
                throw ossException;
            }
        }
        long time5 = System.currentTimeMillis();
        log.info("uploadOSSObjectsTime:5all-:{}", time5 - time0);
        return ossObjectList;
    }

    private OssObject buildOSSObject(MultipartFile file, String module, User user) {
        OssObject ossObject = new OssObject();
        //对象再阿里云上的bucket存储中的key为时间戳+UUID
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat(DateUtil.DATE_PATTERN_SECOND);
        SimpleDateFormat sdfYear = new SimpleDateFormat(DateUtil.datePatternYYYY);
        SimpleDateFormat sdfMonth = new SimpleDateFormat(DateUtil.datePatternYY_MM);

        String objectKey = module + "/" + sdfYear.format(date) + "/" + sdfMonth.format(date) + "/" + sdf.format(date) + "_" + UUID.randomUUID() + Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
        ossObject.setObjectKey(objectKey);

        //带拓展的名称
        String filename = file.getOriginalFilename();
        //文件名过长处理
        if (filename.length() > 250) {
            filename = StringUtils.substring(filename, 0, 250);
        }
        ossObject.setObjectName(filename);
        //对象类型（扩展名）
        String contentType = file.getContentType();
        ossObject.setObjectType(contentType);
        //对象大小
        ossObject.setObjectSize("" + file.getSize());
        //上传人id
        ossObject.setUploadPersonnelId(Long.valueOf(user.getUid()));
        //上传人姓名
        ossObject.setUploadPersonnelName(user.getUserName());
        //上传时间
        ossObject.setUploadTime(LocalDateTime.now());
        //对象状态：1存活, 2已删除
        ossObject.setStatus(OssObject.STATUS_ALIVE);
        return ossObject;
    }

    /**
     * 下载单个对象
     *
     * @param objectId 对象标识
     * @param request  http请求
     * @param response http响应
     */
    @Override
    public void downloadObject(Long objectId, HttpServletRequest request, HttpServletResponse response) throws OSSException {
        // 依据标识从数据库中查询对象名称
        OssObject ossObject = ossObjectMapper.query(objectId);
        if (ossObject == null) {
            log.error("下载失败，对象不存在，objectId = " + objectId);
            OSSException ossException = new OSSException(-1, "对象不存在");
            throw ossException;
        }
        if (OssObject.STATUS_DELETED == ossObject.getStatus()) {
            log.error("下载失败，对象已删除，objectId = " + objectId);
            OSSException ossException = new OSSException(-1, "对象已删除");
            throw ossException;
        }
        //调用阿里云SDK下载对象
        String objectKey = ossObject.getObjectKey();
        if (StringUtils.isNoneBlank(objectKey)) {
            //初始化下载响应头
            OutputStream outputStream = initDownloadResponse(request, response, ossObject);

            boolean res = AliyunOSSUtils.downloadObject(objectKey, outputStream);
            if (!res) {
                log.error("下载失败，访问阿里云OSS失败失败，objectId = " + objectId + ", objectKey = " + objectKey);
                OSSException ossException = new OSSException(-1, "访问阿里云OSS失败");
                throw ossException;
            }
        } else {
            log.error("下载失败，对象ObjectKey为空，objectId = " + objectId + ", objectKey = " + objectKey);
            OSSException ossException = new OSSException(-1, "对象ObjectKey为空");
            throw ossException;
        }

    }

    /**
     * 根据客户端类型初始化下载响应头
     * @param req
     * @param res
     * @param ossObjects
     * @return
     * @throws OSSException
     */
    private OutputStream initDownloadResponse(HttpServletRequest req, HttpServletResponse res, OssObject ossObjects) throws OSSException {
        OutputStream outputStream = null;
        try{
            String objectName = ossObjects.getObjectName();
            //判断浏览器是否为火狐
            String UserAgent = req.getHeader("USER-AGENT").toLowerCase();
            if (UserAgent != null && UserAgent.indexOf("firefox") != -1) {
                // 火狐浏览器 设置编码new String(realName.getBytes("GB2312"), "ISO-8859-1");
                objectName = new String(objectName.getBytes("GB2312"), "ISO-8859-1");
            } else {
                objectName = URLEncoder.encode(objectName, "UTF-8");//encode编码UTF-8 解决大多数中文乱码
                objectName = objectName.replace("+", "%20");//encode后替换空格  解决空格问题
            }

            //设置响应为附件下载模式
            res.setContentType("application/x-download");//设置强制下载
            res.setHeader("Content-Disposition", "attachment;filename=" + objectName);//设置文件名
            outputStream = res.getOutputStream();
        }catch (Exception e){
            log.error("下载失败，下载响应初始化失败", e);
            OSSException ossException = new OSSException(-1, "下载响应初始化失败");
            throw ossException;
        }
        return outputStream;
    }
}
